package com.szh.controller;

import com.google.gson.Gson;
import com.szh.service.WxPayService;
import com.szh.utils.HttpUtils;
import com.szh.utils.WechatPay2ValidatorForRequest;
import com.szh.vo.R;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Api(tags = "网站微信支付APIv3")
@CrossOrigin //跨域
@RestController
@RequestMapping(value = "/api/wxPay")
public class WxPayController {

    private static final Logger LOGGER = LoggerFactory.getLogger(WxPayController.class);

    @Resource
    private WxPayService wxPayService;

    @Resource
    private Verifier verifier;

    /**
     * 调用统一下单API，生成微信支付二维码
     * @param productId 商品id
     * @return 返回支付二维码链接和订单号
     * @throws Exception Exception
     */
    @ApiOperation(value = "调用统一下单API，生成微信支付二维码")
    @PostMapping(value = "/native/{productId}")
    public R nativePay(@ApiParam(name = "productId", value = "商品id", required = true)
                       @PathVariable("productId") Long productId) throws Exception {
        LOGGER.info("商品id ===> {} 发起支付请求", productId);
        //返回支付二维码链接和订单号
        Map<String, Object> map = wxPayService.nativePay(productId);
        return R.ok().setData(map);
    }

    /**
     * 支付通知
     * 微信支付通过支付通知接口将用户支付成功消息通知给商户
     */
    @ApiOperation(value = "支付通知")
    @PostMapping(value = "/native/notify")
    public String nativeNotify(HttpServletRequest request, HttpServletResponse response) {
        Gson gson = new Gson();
        Map<String, String> map = new HashMap<>(); //应答对象
        try {
            //处理通知参数
            String body = HttpUtils.readData(request);
            Map<String, Object> bodyMap = gson.fromJson(body, HashMap.class);
            String requestId = (String) bodyMap.get("id");
            LOGGER.info("支付通知的id ===> {}", requestId);
            LOGGER.info("支付通知的完整数据 ===> {}", body);
            WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest =
                    new WechatPay2ValidatorForRequest(verifier, requestId, body);
            if (!wechatPay2ValidatorForRequest.validate(request)) {
                LOGGER.error("通知验签失败");
                response.setStatus(500);
                map.put("code", "ERROR");
                map.put("message", "通知验签失败");
                return gson.toJson(map);
            }
            LOGGER.info("通知验签成功");
            //处理订单
            wxPayService.processOrder(bodyMap);
            //应答超时，模拟接收微信端的重复通知
//            TimeUnit.SECONDS.sleep(5);
            //成功应答
            response.setStatus(200);
            map.put("code", "SUCCESS");
            map.put("message", "成功");
            return gson.toJson(map);
        } catch (Exception e) {
            e.printStackTrace();
            //失败应答
            response.setStatus(500);
            map.put("code", "ERROR");
            map.put("message", "失败");
            return gson.toJson(map);
        }
    }

    /**
     * 取消订单
     * @param orderNo 订单号
     * @return R
     * @throws Exception Exception
     */
    @ApiOperation(value = "取消订单")
    @PostMapping(value = "/cancel/{orderNo}")
    public R cancel(@ApiParam(name = "orderNo", value = "订单号", required = true)
                    @PathVariable("orderNo") String orderNo) throws Exception {
        LOGGER.info("取消订单");
        wxPayService.cancelOrder(orderNo);
        return R.ok().setMessage("订单已取消");
    }

    /**
     * 查询订单
     * @param orderNo 订单号
     * @return R
     * @throws Exception Exception
     */
    @ApiOperation(value = "查询订单")
    @GetMapping(value = "/query/{orderNo}")
    public R query(@ApiParam(name = "orderNo", value = "订单号", required = true)
                   @PathVariable("orderNo") String orderNo) throws Exception {
        LOGGER.info("查询订单");
        String result = wxPayService.queryOrder(orderNo);
        return R.ok().setMessage("查询成功").data("result", result);
    }

    /**
     * 申请退款
     * @param orderNo 订单号
     * @param reason 退款原因
     * @return R
     * @throws Exception Exception
     */
    @ApiOperation("申请退款")
    @PostMapping("/refunds/{orderNo}/{reason}")
    public R refunds(@ApiParam(name = "orderNo", value = "订单号", required = true)
                     @PathVariable("orderNo") String orderNo,
                     @ApiParam(name = "reason", value = "退款原因", required = true)
                     @PathVariable("reason") String reason) throws Exception {
        LOGGER.info("申请退款");
        wxPayService.refund(orderNo, reason);
        return R.ok();
    }

    /**
     * 查询单笔退款
     * @param refundNo 退款单号
     * @return R
     * @throws Exception Exception
     */
    @ApiOperation("查询单笔退款")
    @GetMapping("/queryRefund/{refundNo}")
    public R queryRefund(@ApiParam(name = "refundNo", value = "退款单号", required = true)
                         @PathVariable("refundNo") String refundNo) throws Exception {
        LOGGER.info("查询退款");
        String result = wxPayService.queryRefund(refundNo);
        return R.ok().setMessage("查询成功").data("result", result);
    }

    /**
     * 退款结果通知
     * 退款状态改变后，微信会把相关退款结果发送给商户
     */
    @ApiOperation("退款结果通知")
    @PostMapping("/refunds/notify")
    public String refundsNotify(HttpServletRequest request, HttpServletResponse response) {
        LOGGER.info("退款通知执行");
        Gson gson = new Gson();
        Map<String, String> map = new HashMap<>(); //应答对象
        try {
            //处理通知参数
            String body = HttpUtils.readData(request);
            Map<String, Object> bodyMap = gson.fromJson(body, HashMap.class);
            String requestId = (String)bodyMap.get("id");
            LOGGER.info("支付通知的id ===> {}", requestId);
            //签名的验证
            WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest
                    = new WechatPay2ValidatorForRequest(verifier, requestId, body);
            if(!wechatPay2ValidatorForRequest.validate(request)){
                LOGGER.error("通知验签失败");
                //失败应答
                response.setStatus(500);
                map.put("code", "ERROR");
                map.put("message", "通知验签失败");
                return gson.toJson(map);
            }
            LOGGER.info("通知验签成功");
            //处理退款单
            wxPayService.processRefund(bodyMap);
            //成功应答
            response.setStatus(200);
            map.put("code", "SUCCESS");
            map.put("message", "成功");
            return gson.toJson(map);
        } catch (Exception e) {
            e.printStackTrace();
            //失败应答
            response.setStatus(500);
            map.put("code", "ERROR");
            map.put("message", "失败");
            return gson.toJson(map);
        }
    }

    /**
     * 申请账单，获取账单url
     * @param billDate 账单日期
     * @param type 账单类型
     * @return R
     * @throws Exception Exception
     */
    @ApiOperation("申请账单，获取账单url")
    @GetMapping("/queryBill/{billDate}/{type}")
    public R queryTradeBill(@ApiParam(name = "billDate", value = "账单日期", required = true)
                            @PathVariable String billDate,
                            @ApiParam(name = "type", value = "账单类型", required = true)
                            @PathVariable String type) throws Exception {
        LOGGER.info("申请账单，获取账单url");
        String downloadUrl = wxPayService.queryBill(billDate, type);
        return R.ok().setMessage("获取账单url成功").data("downloadUrl", downloadUrl);
    }

    /**
     * 下载账单
     * @param billDate 账单日期
     * @param type 账单类型
     * @return R
     * @throws Exception Exception
     */
    @ApiOperation("下载账单")
    @GetMapping("/downloadBill/{billDate}/{type}")
    public R downloadBill(@ApiParam(name = "billDate", value = "账单日期", required = true)
                          @PathVariable String billDate,
                          @ApiParam(name = "type", value = "账单类型", required = true)
                          @PathVariable String type) throws Exception {
        LOGGER.info("下载账单");
        String result = wxPayService.downloadBill(billDate, type);
        return R.ok().data("result", result);
    }
}
